/*

    Licensed under the Apache License, Version 2.0 (the "License"); 
    you may not use this file except in compliance with the License. 
    You may obtain a copy of the License at 
    http://www.apache.org/licenses/LICENSE-2.0 
    Unless required by applicable law or agreed to in writing, software 
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    See the License for the specific language governing permissions and 
    limitations under the License.
      
 */

package jforex.mql.converter;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.filechooser.FileSystemView;

/**
 */
public class MQLConverter {

    public File convert(String fileFrom, String fileTo) throws Exception {
        return convert(fileFrom, fileTo,  "windows-1251");
    }

    public File convert(String fileFrom) throws Exception {
        return convert(fileFrom, getMyStrategiesDefaultDirPath(),  "windows-1251");
    }

    String getMyStrategiesDefaultDirPath() {
        File myDocs = FileSystemView.getFileSystemView().getDefaultDirectory();
        return myDocs.getPath() + File.separator + "My Strategies";
    }

    public File convert(String fileFrom, String fileTo, String encoding) throws Exception {

        File src = new File(fileFrom);
        File targetDir = new File(fileTo);

        if (!src.exists()) {
            throw new Exception("Source file not found.");
        }
        if (!targetDir.exists()) {
            throw new Exception("Target dir not found.");
        }
        if (!targetDir.isDirectory()) {
            throw new Exception("Target dir is a file.");
        }

        String javaClassName = generateJavaClassName(src);

        String convertedContent = convertContent(src, javaClassName, encoding);
        File srcFile = new File(targetDir + File.separator + javaClassName + ".java");
        if (srcFile.isFile() && srcFile.exists()) {
            throw new Exception(srcFile.getAbsolutePath() + " already exists!");
        }
        writeFile(srcFile.getAbsolutePath(), convertedContent.getBytes());
        return srcFile;
    }

    private String convertContent(File src, String javaClassName, String encoding) throws Exception {

        String mql = new String(readFile(src), encoding);

        String variableName = "[a-zA-Z_]+";

        mql = replaceRegExp(mql, "#property", "//#property");
        mql = replaceRegExp(mql, "#include", "//#include");
        mql = replaceRegExp(mql, "#import", "//#import");

        mql = replaceRegExp(mql, "bool ", "boolean ");
        mql = replaceRegExp(mql, "extern ", "@Configurable\\(\"\"\\) public ");
        mql = replaceRegExp(mql, "FALSE", "false");
        mql = replaceRegExp(mql, "TRUE", "true");
        mql = replaceRegExp(mql, "False", "false");
        mql = replaceRegExp(mql, "True", "true");

        mql = replaceRegExp(mql, "NULL", "null");
        mql = replaceRegExp(mql, "string", "String");
        mql = replaceRegExp(mql, "color", "Color");
        mql = replaceRegExp(mql, "datetime", "long");

        mql = replaceRegExp(mql, "#define\\s*(" + variableName + ")\\s*\\-*(\\d+)", "int $1 = $2;");

        mql = replaceRegExp(mql, "Time\\[(\\d+)\\]", "Time\\($1\\)");

        mql = replaceRegExp(mql, "C'(\\d+),\\s*(\\d+),\\s*(\\d+)'\\s*;", "new Color($1,$2,$3);");
        
        mql = replaceRegExp(mql, "Close\\[(\\d+)\\]", "Close\\($1\\)");
        mql = replaceRegExp(mql, "High\\[(\\d+)\\]", "High\\($1\\)");
        mql = replaceRegExp(mql, "Open\\[(\\d+)\\]", "Open\\($1\\)");
        mql = replaceRegExp(mql, "Low\\[(\\d+)\\]", "Low\\($1\\)");

        //instruments replacement

        List<String> allInstruments = new ArrayList<String>();

        allInstruments.add("AUDJPY");
        allInstruments.add("AUDNZD");
        allInstruments.add("AUDUSD");
        allInstruments.add("CADJPY");
        allInstruments.add("CHFJPY");
        allInstruments.add("EURAUD");
        allInstruments.add("EURCAD");
        allInstruments.add("EURCHF");
        allInstruments.add("EURDKK");
        allInstruments.add("EURGBP");
        allInstruments.add("EURHKD");
        allInstruments.add("EURJPY");
        allInstruments.add("EURNOK");
        allInstruments.add("EURSEK");
        allInstruments.add("EURUSD");
        allInstruments.add("GBPCHF");
        allInstruments.add("GBPJPY");
        allInstruments.add("GBPUSD");
        allInstruments.add("NZDUSD");
        allInstruments.add("USDCAD");
        allInstruments.add("USDCHF");
        allInstruments.add("USDDKK");
        allInstruments.add("USDHKD");
        allInstruments.add("USDJPY");
        allInstruments.add("USDMXN");
        allInstruments.add("USDNOK");
        allInstruments.add("USDSEK");
        allInstruments.add("USDSGD");
        allInstruments.add("USDTRY");

        for (String instrument : allInstruments) {
            mql = replaceRegExp(mql, "\"" + instrument + "\"", "Instrument.fromString(\"" + (instrument.substring(0, 3) + "/" + instrument.substring(3, 6)) + "\")");
        }

        mql = replaceRegExp(mql, "((?:double)|(?:int))\\s+(" + variableName + ")\\s*;", "$1 $2 = 0;");
        mql = replaceRegExp(mql, "boolean\\s+(" + variableName + ");", "boolean $1 = false;");

        //double tick_data[2][20];  <-- such definition doesn't work in java. we take 3,2,1 dimentional arrays
        mql = replaceRegExp(mql, "((?:double)|(?:int))\\s+(" + variableName + ")\\[(\\d+)\\]\\[(\\d+)\\]\\[(\\d+)\\]", "$1[][][] $2 = new double[$3][$4][$5]");
        mql = replaceRegExp(mql, "((?:double)|(?:int))\\s+(" + variableName + ")\\[(\\d+)\\]\\[(\\d+)\\]", "$1[][] $2 = new double[$3][$4]");
        mql = replaceRegExp(mql, "((?:double)|(?:int))\\s+(" + variableName + ")\\[(\\d+)\\]", "$1[] $2 = new double[$3]");

        mql = replaceRegExp(mql, "((?:void)|(?:int))\\s+((?:deinit)|(?:init)|(?:start))", "protected int $2");

        String result = genTemplate(javaClassName, mql);
        return result;

    }

    private String replaceRegExp(String src, String regexpString, String replacement) throws Exception {
        Pattern pattern = Pattern.compile(regexpString, Pattern.DOTALL);

        Matcher matcher = pattern.matcher(src);

        return matcher.replaceAll(replacement);

    }

    private String generateJavaClassName(File src) {
        String fileName = src.getName();
        if (fileName.indexOf(".") != -1) {
            fileName = fileName.substring(0, fileName.lastIndexOf("."));
        }

        fileName = fileName.replaceAll(" ", "");
        fileName = fileName.replaceAll("\\.", "");
        fileName = fileName.replaceAll("[\\(\\)]", "");
        fileName = fileName.replaceAll("\\-", "");
        fileName = fileName.replaceAll("\\[", "");
        fileName = fileName.replaceAll("\\]", "");
        fileName = fileName.replaceAll("\\%", "");
       char c = fileName.charAt(0);
       try{
           Integer.parseInt(c+"");
           fileName = fileName.substring(1);
       }catch(Exception e){
           
       }

        return fileName;
    }

    private String genTemplate(String name, String content) {
        String version = getClass().getPackage().getImplementationVersion();
        if (version == null) {
            version = "";
        }
        String template = "package jforex.converted;\n" + "import java.awt.Color;\n" + "import jforex.mql.MQLBridge;\n" + "import com.dukascopy.api.*;\n" + "@Library(\"jftoolbox-" + version + ".jar\")\n" + "public class " + name + " extends MQLBridge {\n" + content + "\n" + "public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException{}" + "/**/" + "};";
        return template;
    }

    public static byte[] readFile(File f) {

        if (!f.exists())
            return new byte[0];
        int size = (int) f.length();
        if (size <= 0)
            return new byte[0];
        byte[] data = new byte[size];
        try {
            FileInputStream fis = new FileInputStream(f);
            int bytes_read = 0;
            while (bytes_read < size)
                bytes_read += fis.read(data, bytes_read, size - bytes_read);
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[0];
        }
        return data;
    }

    public static boolean writeFile(String filePath, byte[] bs) {
        FileOutputStream fileOutputStream = null;
        try {
            File file = new File(filePath);
            if (!file.exists()) {
                file.createNewFile();
            }
            fileOutputStream = new FileOutputStream(file);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bs);
            //turboPipe(byteArrayInputStream, fileOutputStream);
            int bytes_read;
            byte[] buffer = new byte[4 * 1024];
            while (true) {
                bytes_read = byteArrayInputStream.read(buffer);

                if (bytes_read == -1) {
                    break;
                }
                fileOutputStream.write(buffer, 0, bytes_read);

            }
            return true;

        } catch (Throwable e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                fileOutputStream.close();
            } catch (Exception e1) {
            }
        }

    }

}
